// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use crate::*;
use pyo3_stub_gen::{derive::*, inventory::submit, module_doc};

module_doc!(
    "opendal.services",
    r#"
Services.

>> DO NOT EDIT THIS FILE MANUALLY <<

This file is automatically generated by just recipe with name `stub-gen`
alongwith the rest of the stubs.

See justfile at path ``../../justfile`` for more details.
"#
);

#[gen_stub_pyclass_enum]
#[pyclass(
    eq,
    eq_int,
    dict,
    hash,
    frozen,
    name = "Scheme",
    module = "opendal.services"
)]
#[pyo3(rename_all = "PascalCase")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PyScheme {
{%- for name in srvs %}
    #[cfg(feature = "{{service_to_feature(name)}}")]
    {{ service_to_pascal(name) }},
{%- endfor %}
}

#[gen_stub_pymethods]
#[pymethods]
impl PyScheme {
    #[getter]
    pub fn name(&self) -> String {
        format!("{:?}", &self)
    }

    #[getter]
    pub fn value(&self) -> &'static str {
        let scheme: ocore::Scheme = (*self).into();
        scheme.into_static()
    }
}

{% for srv in srvs %}
submit! {
    gen_methods_from_python! {
        r#"
        import builtins
        import typing
        import typing_extensions
        import opendal.services
        class Operator:
            @overload
            def __new__(cls,
                scheme: typing.Union[opendal.services.Scheme.{{ service_to_pascal(srv) }}, typing.Literal["{{ snake_to_kebab_case(srv) }}"]],
                /,
                {%- if srvs[srv].config %}
                *,
                {%- endif %}
                {%- for field in srvs[srv].config %}
                {{field.name}}: {{make_python_type(field.value)}}{% if field.optional %} = ...{% endif %},
                {%- endfor %}
            ) -> typing_extensions.Self:
                r"""
                Create a new `Operator` for `{{ snake_to_kebab_case(srv) }}` service.
                {%- if srvs[srv].config %}

                Parameters
                ----------
                {%- for field in srvs[srv].config %}
                {{ make_pydoc_param_header(field.name, field.value, field.optional) }}
{{ make_pydoc_param(field.comments, field.value) }}
                {%- endfor %}
                {%- endif %}
                Returns
                -------
                Operator
                    The new `Operator` for `{{ snake_to_kebab_case(srv) }}` service
                """
        "#
    }
}

{% endfor %}
{% for srv in srvs %}
submit! {
    gen_methods_from_python! {
        r#"
        import builtins
        import typing
        import typing_extensions
        import opendal.services
        class AsyncOperator:
            @overload
            def __new__(cls,
                scheme: typing.Union[opendal.services.Scheme.{{ service_to_pascal(srv) }}, typing.Literal["{{ snake_to_kebab_case(srv) }}"]],
                /,
                {%- if srvs[srv].config %}
                *,
                {%- endif %}
                {%- for field in srvs[srv].config %}
                {{field.name}}: {{make_python_type(field.value)}}{% if field.optional %} = ...{% endif %},
                {%- endfor %}
            ) -> typing_extensions.Self:
                r"""
                Create a new `AsyncOperator` for `{{ snake_to_kebab_case(srv) }}` service.
                {%- if srvs[srv].config %}

                Parameters
                ----------
                {%- for field in srvs[srv].config %}
                {{ make_pydoc_param_header(field.name, field.value, field.optional) }}
{{ make_pydoc_param(field.comments, field.value) }}
                {%- endfor %}
                {%- endif %}
                Returns
                -------
                AsyncOperator
                    The new `AsyncOperator` for `{{ snake_to_kebab_case(srv) }}` service
                """
        "#
    }
}
{% endfor %}
// --- Conversion Macro ---
macro_rules! impl_enum_from {
    ($src:ty => $dst:ty { $(
        $(#[$cfg:meta])?
        $variant:ident
    ),* $(,)? }) => {
        impl From<$src> for $dst {
            fn from(value: $src) -> Self {
                match value {
                    $(
                        $(#[$cfg])?
                        <$src>::$variant => <$dst>::$variant,
                    )*
                    #[allow(unreachable_patterns)]
                    _ => unreachable!(
                        "Unsupported scheme variant: {:?}. \
                         This likely means a new variant was added to `{}` \
                         but `PyScheme` or the generated bindings were not updated.",
                        value,
                        stringify!($src)
                    ),
                }
            }
        }
    };
}

// --- PyScheme -> ocore::Scheme ---
impl_enum_from!(
    PyScheme => ocore::Scheme {
{%- for name in srvs %}
    #[cfg(feature = "{{ service_to_feature(name) }}")]
    {{ service_to_pascal(name) }},
{%- endfor %}
    }
);

// --- ocore::Scheme -> PyScheme ---
impl_enum_from!(
    ocore::Scheme => PyScheme {
{%- for name in srvs %}
    #[cfg(feature = "{{ service_to_feature(name) }}")]
    {{ service_to_pascal(name) }},
{%- endfor %}
    }
);
